#include "mcu_flash.h"
#include "mcu.h"

//FLASH操作部分宏定义	
#define FLASH_RASS_KEY1 (0x56) /*!< First RASS key */
#define FLASH_RASS_KEY2 (0xAE) /*!< Second RASS key */

#ifdef _FLASH_USE_BLOCK_WRITE
    #define IN_RAM   __ramfunc 
#else
    #define IN_RAM    //不使用块写入时
#endif

/****************************************************************************
* 名    称：void Unlock(void)
* 功    能：解锁STM8S的Flash(包括代码区域和EEPROM区域)
* 入口参数：-
* 出口参数：-
* 说    明：-
* 调用方法: Unlock(FLASH_MEMTYPE_DATA);
****************************************************************************/
static void Unlock(flash_mem_type_t type)
{
	if(type == FLASH_MEMTYPE_PROG)
	{
		FLASH_PUKR = FLASH_RASS_KEY1;
		FLASH_PUKR = FLASH_RASS_KEY2;
		while(!(FLASH_IAPSR & MASK_FLASH_IAPSR_PUL));		//等待解锁成功
	}
	/* Unlock data memory */
	else
	{
		FLASH_DUKR = FLASH_RASS_KEY2; /* Warning: keys are reversed on data memory !!! */
		FLASH_DUKR = FLASH_RASS_KEY1;
		while(!(FLASH_IAPSR & MASK_FLASH_IAPSR_DUL));		//等待解锁成功
	}
}
/****************************************************************************
* 名    称：FLASH_Lock
* 功    能：上锁STM8S的Flash(包括代码区域和EEPROM区域)
* 入口参数：-
* 出口参数：-
* 说    明：-
* 调用方法: Lock(FLASH_MEMTYPE_DATA);
****************************************************************************/
static void Lock(flash_mem_type_t type)
{
	FLASH_IAPSR &= (uint8_t)type;
}
/****************************************************************************
* 名    称：FLASH_WaitForLastOperation
* 功    能：等待操作完成
* 入口参数：-
* 出口参数：
* 说    明：-
* 调用方法: flag = WaitForLastOperation(FLASH_MEMTYPE_DATA);
****************************************************************************/
IN_RAM static flash_status_t WaitForLastOperation(flash_mem_type_t Type)
{
    uint8_t  flagstatus = 0X00;
    uint16_t timeout = OPERATION_TIMEOUT; 

#if defined(STM8S208)||defined(STM8S207)||defined(STM8S007)||defined(STM8S105)||\
    defined(STM8S005)||defined(STM8AF52Ax)||defined(STM8AF62Ax)||defined(STM8AF626x)  
	if(Type == FLASH_MEMTYPE_PROG)
    {
      while((flagstatus == 0x00) && (timeout != 0x00))
      {
        flagstatus = (uint8_t)(FLASH_IAPSR & (uint8_t)(MASK_FLASH_IAPSR_EOP |MASK_FLASH_IAPSR_WR_PG_DIS));
        timeout--;
      }
    }
    else
    {
      while((flagstatus == 0x00) && (timeout != 0x00))
      {
        flagstatus = (uint8_t)(FLASH_IAPSR & (uint8_t)(MASK_FLASH_IAPSR_HVOFF |MASK_FLASH_IAPSR_WR_PG_DIS));
        timeout--;
      }
    }
#else //STM8S103, STM8S903  
	UNUSED(Type);
	while((flagstatus == 0x00) && (timeout != 0x00))
	{
		flagstatus = (uint8_t)(FLASH_IAPSR & (MASK_FLASH_IAPSR_EOP | MASK_FLASH_IAPSR_WR_PG_DIS));
		timeout--;
	}
#endif 
    if(timeout==0)flagstatus = FLASH_STATUS_TIMEOUT;
	return((flash_status_t)flagstatus);
}

//向FLASH写入一个字节，注意操作之前要解锁，需要确保操作结束，结束后上锁
//void FLASH_WriteByte(uint32_t Address, uint8_t Data)
//{
//  *(PointerAttr uint8_t*) (faddr_t)Address = Data;
//}

//uint8_t FLASH_ReadByte(uint32_t Address)
//{
//  return(*(PointerAttr uint8_t *) (faddr_t)Address); 
//}

//向FLASH写入一个Word（4字节），注意操作之前要解锁，需要确保操作结束，结束后上锁
//字的起始地址必须以0,4,8,C作为结尾
// void FLASH_WriteWord(faddr_t Address, uint32_t Data)
// {
//   /* Enable Word Write Once */
//   FLASH_CR2 |= MASK_FLASH_CR2_WPRG;
//   FLASH_NCR2 &= (uint8_t)(~MASK_FLASH_NCR2_NWPRG);
  
//   /* Write one byte - from lowest address*/
//   *((PointerAttr uint8_t*)Address)       = *((uint8_t*)(&Data));
//   /* Write one byte*/
//   *(((PointerAttr uint8_t*)Address) + 1) = *((uint8_t*)(&Data)+1); 
//   /* Write one byte*/    
//   *(((PointerAttr uint8_t*)Address) + 2) = *((uint8_t*)(&Data)+2); 
//   /* Write one byte - from higher address*/
//   *(((PointerAttr uint8_t*)Address) + 3) = *((uint8_t*)(&Data)+3); 
// }


void Flash_WriteOptionByte(uint16_t Address, uint8_t Data)
{
    Unlock(FLASH_MEMTYPE_PROG);

  /* Enable write access to option bytes */
  FLASH_CR2 |= MASK_FLASH_CR2_OPT;
  FLASH_NCR2 &= (uint8_t)(~MASK_FLASH_NCR2_NOPT);
  
  /* check if the option byte to program is ROP*/
  if(Address == 0x4800)
  {
    /* Program option byte*/
    *((__near uint8_t*)Address) = Data;
  }
  else
  {
    /* Program option byte and his complement */
    *((__near uint8_t*)Address) = Data;
    *((__near uint8_t*)((uint16_t)(Address + 1))) = (uint8_t)(~Data);
  }
  WaitForLastOperation(FLASH_MEMTYPE_PROG);
  
  /* Disable write access to option bytes */
  FLASH_CR2 &= (uint8_t)(~MASK_FLASH_CR2_OPT);
  FLASH_NCR2 |= MASK_FLASH_NCR2_NOPT;

  Lock(FLASH_MEMTYPE_PROG);
}

/**
  * @brief  Reads one option byte
  * @param  Address  option byte address to read.
  * @retval Option byte read value + its complement
  */
// uint16_t FLASH_ReadOptionByte(uint16_t Address)
// {
//   uint8_t value_optbyte, value_optbyte_complement = 0;
//   uint16_t res_value = 0;
    
//   value_optbyte = *((__near uint8_t*)Address); /* Read option byte */
//   value_optbyte_complement = *(((__near uint8_t*)Address) + 1); /* Read option byte complement */
  
//   /* Read-out protection option byte */
//   if(Address == 0x4800)	 
//   {
//     res_value =	 value_optbyte;
//   }
//   else
//   {
//     if(value_optbyte == (uint8_t)(~value_optbyte_complement))
//     {
//       res_value = (uint16_t)((uint16_t)value_optbyte << 8);
//       res_value = res_value | (uint16_t)value_optbyte_complement;
//     }
//     else
//     {
//       res_value = 0x5555;	//表示错误
//     }
//   }
//   return(res_value);
// }

/**
  * @brief  Returns the Boot memory size in bytes
  * @param  None
  * @retval Boot memory size in bytes
  */
uint32_t Flash_GetBootSize(void)
{
  uint32_t temp = 0;
  
  /* Calculates the number of bytes */
  temp = (uint32_t)((uint32_t)FLASH_FPR * (uint32_t)512);
  
  /* Correction because size of 127.5 kb doesn't exist */
  if(FLASH_FPR == 0xFF)
  {
    temp += 512;
  }

  return(temp);
}

#ifdef _FLASH_USE_BLOCK_WRITE
/**
  * @brief  写一整块数据
  * @note   This function should be executed from RAM.
  * @param  Address : 块的首地址，必须是首地址
  * @param  Buffer : Pointer to buffer containing source data.
  * @retval None.
  */
IN_RAM void Flash_WriteBlock(faddr_t Address, const u8 *Buffer)
{
    u8 Count = 0;
    flash_mem_type_t type;

    if(Address >= FLASH_PROG_START_PHYSICAL_ADDRESS) type = FLASH_MEMTYPE_PROG;
    else type = FLASH_MEMTYPE_DATA;

    Unlock(type);

    FLASH_CR2 |= MASK_FLASH_CR2_PRG;
    FLASH_NCR2 &= (uint8_t)(~MASK_FLASH_NCR2_NPRG); // 使能标准块编程

    for(Count = 0; Count < FLASH_BLOCK_SIZE; Count++)   // 数据被实际上写入到MCU内部专用缓存
    {
    *((PointerAttr u8*)Address + Count) = Buffer[Count];
    }                   
    // 当目标块的最后一个字节被装载到缓存后，编程就自动开始了

    WaitForLastOperation(type);	//等待操作结束

    Lock(type);
}

// 使用 读改写 方法，安全的（不破坏数据）向1个块写入部分数据
// 需保证写入数据在同一块内
static void SafeWriteBlock(faddr_t addr,const u8 * dat,u8 len)
{
  faddr_t addr_block;
  u8 addr_diff, i;
  u8 buff[FLASH_BLOCK_SIZE];

  addr_block = (addr / FLASH_BLOCK_SIZE) * FLASH_BLOCK_SIZE;  //块地址（块的首地址）
  addr_diff = addr % FLASH_BLOCK_SIZE;  //起始地址在块的偏移

  //读取整块原数据
  for(i = 0;i < FLASH_BLOCK_SIZE; i++)
  {
    buff[i] = *(PointerAttr u8 *)(faddr_t)(addr_block + i); 
  }
  //修改需要写入的新数据
  for(i = 0; i < len; i++)
	{
			buff[addr_diff + i] = dat[i];
	}
  //写入修改后的整页数据
  Flash_WriteBlock(addr_block, buff);
}

#endif


//安全的向FLASH的任意地址（包含EEPROM区域）写入任意数据
// addr: 地址
// dat : 数据
// len : 长度
void Flash_Write(faddr_t addr,const u8 * dat,u16 len)
{
  //写入数据覆盖的地址范围中，第一个块和最后一个块要注意数据备份
  u8 free_space;

  free_space = FLASH_BLOCK_SIZE - (addr % FLASH_BLOCK_SIZE);  //首块的剩余空间
  if(len > free_space)  //超出了一块,先写这一块的剩余空间
  {
    SafeWriteBlock(addr, dat, free_space);
    dat += free_space;
    addr += free_space;
    len -= free_space;
  }
  while(len >= FLASH_BLOCK_SIZE) //剩余的长度大于或等于1个块，直接写整块
  {
    Flash_WriteBlock(addr, dat);
    dat += FLASH_BLOCK_SIZE;
    addr += FLASH_BLOCK_SIZE;
    len -= FLASH_BLOCK_SIZE;
  }
  if(len > 0) //写入剩余最后不足一个块的数据
  {
    SafeWriteBlock(addr, dat, len);  
  }
}



#ifdef _FLASH_SUPORT_IAR_EEPROM

///////////////////以下三个函数,以支持IAR独有的 __eeprom 关键字操作////////////////////

//EEPROM完成操作等待
void __eeprom_wait_for_last_operation(void)
{
	WaitForLastOperation(FLASH_MEMTYPE_DATA);	//等待操作结束
}
//EEPROM,在指定位置写入一个字节数据
//addr:地址
//data:要写入的数据
void __eeprom_program_byte(unsigned short addr,unsigned char data)
{
	WaitForLastOperation(FLASH_MEMTYPE_DATA);	//等待操作结束
	Unlock(FLASH_MEMTYPE_DATA); 
	*(PointerAttr unsigned char *)addr=data;
	WaitForLastOperation(FLASH_MEMTYPE_DATA);	//等待操作结束
	Lock(FLASH_MEMTYPE_DATA);
}
		   
//3101版本编译器优化等级过高后，自带EEPROM函数有问题，使用这个函数替代。
//void $Sub$$__eeprom_write_16(unsigned short addr,unsigned short data)
//{
//	EEPROM_WaitForLastOperation();	//等待操作结束
//	Unlock(); 
//	*(PointerAttr unsigned char*)addr=(unsigned char)(data>>8);  
//	EEPROM_WaitForLastOperation();	//等待操作结束
//	*(PointerAttr unsigned char*)(addr+1)=(unsigned char)(data&0X00FF);  
//	EEPROM_WaitForLastOperation();	//等待操作结束
//	EEPROM_Lock();   
//}
		   
//EEPROM,在指定位置写入一个long型数据(4字节)
//addr:地址
//data:要写入的long型数据
//void __eeprom_program_long(unsigned short addr,unsigned long data)
//{
//	WaitForLastOperation(FLASH_MEMTYPE_DATA);	//等待操作结束
//	Unlock(FLASH_MEMTYPE_DATA); 
//	FLASH_WriteWord(addr, data);			//地址对齐问题待测试
//	WaitForLastOperation(FLASH_MEMTYPE_DATA);	//等待操作结束
//	Lock(FLASH_MEMTYPE_DATA);
//} 

void __eeprom_program_long(unsigned short addr,unsigned long data)
{
	WaitForLastOperation(FLASH_MEMTYPE_DATA);	//等待操作结束
  Unlock(FLASH_MEMTYPE_DATA); 
	*(PointerAttr unsigned char*)addr=(unsigned char)(data>>24);  
	WaitForLastOperation(FLASH_MEMTYPE_DATA);	//等待操作结束
	*(PointerAttr unsigned char*)(addr+1)=(unsigned char)(data>>16);  
	WaitForLastOperation(FLASH_MEMTYPE_DATA);	//等待操作结束
	*(PointerAttr unsigned char*)(addr+2)=(unsigned char)(data>>8);  
	WaitForLastOperation(FLASH_MEMTYPE_DATA);	//等待操作结束
	*(PointerAttr unsigned char*)(addr+3)=(unsigned char)(data&0XFF);  
	WaitForLastOperation(FLASH_MEMTYPE_DATA);	//等待操作结束
	Lock(FLASH_MEMTYPE_DATA);
} 

#endif
